﻿// (c) Copyright 2013 Mike Riedel.
// This source is subject to the Microsoft Public License (Ms-PL).
// All other rights reserved.

using System;
using System.Linq;
using FreshMeat.Storage;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Storage.More.Test
{
   public abstract class CommonStoreTest
   {
      [TestMethod]
      public void SaveDoesNotModifyInstance()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data { Id = 1, Name = "One", UniqueId = new Guid("c271739d941a40ecbd506e029f59d230") };

            context.Save(data.Id, data);

            Assert.AreEqual(1, data.Id, "Id is wrong.");
            Assert.AreEqual("One", data.Name, "Name is wrong.");
            Assert.AreEqual(new Guid("c271739d941a40ecbd506e029f59d230"), data.UniqueId, "UniqueId is wrong.");
         }
      }

      [TestMethod]
      public void SaveIndicatesPriorExistence()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};

            var result1 = context.Save(data.Id, data);
            var result2 = context.Save(data.Id, data);
            var result3 = context.Save(data.Id, data);

            Assert.IsFalse(result1, "First save should return false because data didn't already exist.");
            Assert.IsTrue(result2, "Second save should return true because data already existed.");
            Assert.IsTrue(result3, "Third save should return true because data already existed.");
         }
      }

      //[TestMethod]
      //public void SaveManyIndicatesPriorExistence()
      //{
      //   var store = CreateStore();
      //   using (var context = store.CreateContext())
      //   {
      //      var data1 = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
      //      var data2 = new Data {Id = 2, Name = "Two", UniqueId = Guid.NewGuid()};
      //      var data3 = new Data {Id = 3, Name = "Three", UniqueId = Guid.NewGuid()};
      //      var data4 = new Data {Id = 4, Name = "Four", UniqueId = Guid.NewGuid()};
      //      var data5 = new Data {Id = 5, Name = "Five", UniqueId = Guid.NewGuid()};

      //      var count1 = context.Save(new[] {data1, data2, data3, data4});
      //      var count2 = context.Save(new[] {data4, data2, data3, data1});
      //      var count3 = context.Save(new[] {data5, data4, data3, data2});

      //      Assert.AreEqual(0, count1, "First save should have storerted 0 pre-existing datas.");
      //      Assert.AreEqual(4, count2, "Second save should have storerted 4 pre-existing datas.");
      //      Assert.AreEqual(3, count3, "Third save should have storerted 3 pre-existing datas.");
      //   }
      //}

      [TestMethod]
      public void SaveUpdatesPreviousSave()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};

            context.Save(data.Id, data);
            data.Name = "OneAgain";
            context.Save(data.Id, data);
            Data loaded;
            context.Load(data.Id, out loaded);

            Assert.AreEqual(1, data.Id, "Id should not have changed.");
            Assert.AreEqual("OneAgain", data.Name, "Name should have changed.");
            Assert.AreEqual(data.UniqueId, loaded.UniqueId, "UniqueId should not have changed.");
         }
      }

      [TestMethod]
      public void LoadedInstanceIsNotSavedInstance()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};

            context.Save(data.Id, data);
            Data loaded;
            context.Load(data.Id, out loaded);

            Assert.AreNotSame(data, loaded, "Loaded instance cannot match saved instance.");
         }
      }

      [TestMethod]
      public void DifferentInstanceWithEveryLoad()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};

            context.Save(data.Id, data);
            Data loaded1;
            context.Load(data.Id, out loaded1);
            Data loaded2;
            context.Load(data.Id, out loaded2);

            Assert.AreNotSame(loaded1, loaded2, "Two loaded instances cannot be the same.");
         }
      }

      [TestMethod]
      public void LoadedDataMatchesSavedData()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
            context.Save(data.Id, data);

            Data loaded;
            context.Load(data.Id, out loaded);

            Assert.AreEqual(data.Id, loaded.Id, "Id is wrong.");
            Assert.AreEqual(data.Name, loaded.Name, "Name is wrong.");
            Assert.AreEqual(data.UniqueId, loaded.UniqueId, "UniqueId is wrong.");
         }
      }

      [TestMethod]
      public void LoadReturnsFalseWhenNotFound()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {

            Data loaded;
            var found = context.Load(100, out loaded);

            Assert.IsFalse(found, "Load should have returned false because the item did not exist.");
         }
      }

      [TestMethod]
      public void LoadReturnsTrueWhenFound()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
            context.Save(data.Id, data);

            Data loaded;
            var found = context.Load(1, out loaded);

            Assert.IsTrue(found, "Load should have returned true because the item existed.");
         }
      }

      //[TestMethod]
      //[ExpectedException(typeof(NotFoundException), "Should have thrown exception because at least one requested data did not exist.")]
      //public void LoadManyThrowsWhenNotFound()
      //{
      //   var store = CreateStore();
      //   using (var context = store.CreateContext())
      //   {
      //      var data = new Data {Id = 2, Name = "Two", UniqueId = Guid.NewGuid()};
      //      context.Save(data);

      //      context.Load(new[] {1, 2, 3});
      //   }
      //}

      [TestMethod]
      public void LoadAllReturnsNothing()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {

            var result = context.LoadMany();

            Assert.IsNotNull(result, "Load should not return null.");
            Assert.AreEqual(0, result.Count(), "Load should return empty enumerable when there are no datas.");
         }
      }

      [TestMethod]
      public void LoadAllReturnsEverything()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data1 = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
            var data2 = new Data {Id = 2, Name = "Two", UniqueId = Guid.NewGuid()};
            var data3 = new Data {Id = 3, Name = "Three", UniqueId = Guid.NewGuid()};
            var data4 = new Data {Id = 4, Name = "Four", UniqueId = Guid.NewGuid()};
            context.Save(data1.Id, data1);
            context.Save(data2.Id, data2);
            context.Save(data3.Id, data3);
            context.Save(data4.Id, data4);

            var result = context.LoadMany();

            Assert.IsNotNull(result, "Load should not return null.");
            Assert.AreEqual(4, result.Count(), "Load should return all available datas.");
         }
      }

      [TestMethod]
      public void DeleteIndicatesPriorExistence()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};

            var result1 = context.Delete(1);
            context.Save(data.Id, data);
            var result2 = context.Delete(1);
            var result3 = context.Delete(1);

            Assert.IsFalse(result1, "First delete should return false because data didn't exist.");
            Assert.IsTrue(result2, "Second delete should return true because data did exist.");
            Assert.IsFalse(result3, "Third delete should return false because data no longer exists.");
         }
      }

      //[TestMethod]
      //public void DeleteManyIndicatesPriorExistence()
      //{
      //   var store = CreateStore();
      //   using (var context = store.CreateContext())
      //   {
      //      var data1 = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
      //      var data2 = new Data {Id = 2, Name = "Two", UniqueId = Guid.NewGuid()};
      //      var data3 = new Data {Id = 3, Name = "Three", UniqueId = Guid.NewGuid()};
      //      var data4 = new Data {Id = 4, Name = "Four", UniqueId = Guid.NewGuid()};
      //      var data5 = new Data {Id = 5, Name = "Five", UniqueId = Guid.NewGuid()};
      //      context.Save(new[] {data1, data2, data3, data4, data5});

      //      var count1 = context.Delete(new[] {1, 3});
      //      var count2 = context.Delete(new[] {1, 2, 3});
      //      var count3 = context.Delete(new[] {1, 2, 3, 4, 5});

      //      Assert.AreEqual(2, count1, "First dleete should have storerted 2 actual deletes.");
      //      Assert.AreEqual(1, count2, "Second delete should have storerted 1 actual delete.");
      //      Assert.AreEqual(2, count3, "Third delete should have storerted 2 actual deletes.");
      //   }
      //}

      [TestMethod]
      public void DeletePreventsLoad()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
            context.Save(data.Id, data);

            context.Delete(1);
            Data value;
            var found = context.Load(1, out value);

            Assert.IsFalse(found, "Load should have returned false because item was deleted.");
         }
      }

      [TestMethod]
      public void SaveWorksNormallyAfterDelete()
      {
         var store = CreateStore();
         using (var context = store.CreateContext())
         {
            var data = new Data {Id = 1, Name = "One", UniqueId = Guid.NewGuid()};
            context.Save(data.Id, data);

            context.Delete(1);
            var result = context.Save(data.Id, data);

            Assert.IsFalse(result, "Save should have returned false to indicate data did NOT exist.");
         }
      }

      /// <summary>
      /// When implemented in a derived class, returns a fresh storesitory instance.
      /// </summary>
      /// <returns></returns>
      protected abstract IStore<int, Data> CreateStore();
   }
}